// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现语法树结构的打印。

package syntax

import (
	"fmt"
	"io"
	"reflect"
	"unicode"
	"unicode/utf8"
)

// Fdump转储根在n到w的语法树的结构。
// 用于调试目的；没有特定的输出格式
// 这是有保证的。
func Fdump(w io.Writer, n Node) (err error) {
	p := dumper{
		output: w,
		ptrmap: make(map[Node]int),
		last:   '\n', // 在第一行强制打印行号
	}

	defer func() {
		if e := recover(); e != nil {
			err = e.(writeError).err // 如果不是书写错误，我们会感到恐慌
		}
	}()

	if n == nil {
		p.printf("nil\n")
		return
	}
	p.dump(reflect.ValueOf(n), n)
	p.printf("\n")

	return
}

type dumper struct {
	output io.Writer
	ptrmap map[Node]int // 节点->转储线编号
	indent int          // 当前缩进级别
	last   byte         // 写入处理的最后一个字节
	line   int          // 当前行号
}

var indentBytes = []byte(".  ")

func (p *dumper) Write(data []byte) (n int, err error) {
	var m int
	for i, b := range data {
		// 不变量：已写入数据[0:n]
		if b == '\n' {
			m, err = p.output.Write(data[n : i+1])
			n += m
			if err != nil {
				return
			}
		} else if p.last == '\n' {
			p.line++
			_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
			if err != nil {
				return
			}
			for j := p.indent; j > 0; j-- {
				_, err = p.output.Write(indentBytes)
				if err != nil {
					return
				}
			}
		}
		p.last = b
	}
	if len(data) > n {
		m, err = p.output.Write(data[n:])
		n += m
	}
	return
}

// writeError包装本地捕获的写入错误，以便我们能够区分
// 他们来自真正的恐慌，我们不想返回错误。
type writeError struct {
	err error
}

// printf是一个方便的包装器，用于处理打印错误。
func (p *dumper) printf(format string, args ...interface{}) {
	if _, err := fmt.Fprintf(p, format, args...); err != nil {
		panic(writeError{err})
	}
}

// dump打印x的内容。
// 如果x是结构s的反射值，其中&s
// 实现节点，则应为n传递&s-
// 这允许打印未报告的跨度和宽度
// 按字段对嵌入式isNode的字段进行注释
// 调用Span（）和Comment（）而不是使用
// 反射
func (p *dumper) dump(x reflect.Value, n Node) {
	switch x.Kind() {
	case reflect.Interface:
		if x.IsNil() {
			p.printf("nil")
			return
		}
		p.dump(x.Elem(), nil)

	case reflect.Ptr:
		if x.IsNil() {
			p.printf("nil")
			return
		}

		// 标识符的特殊情况，无附加注释（普通情况）
		if x, ok := x.Interface().(*Name); ok {
			p.printf("%s @ %v", x.Value, x.Pos())
			return
		}

		p.printf("*")
		// 字段可以共享类型表达式和声明
		// 可能共享同一组-使用ptrmap跟踪
		// 已打印的节点数。
		if ptr, ok := x.Interface().(Node); ok {
			if line, exists := p.ptrmap[ptr]; exists {
				p.printf("(Node @ %d)", line)
				return
			}
			p.ptrmap[ptr] = p.line
			n = ptr
		}
		p.dump(x.Elem(), n)

	case reflect.Slice:
		if x.IsNil() {
			p.printf("nil")
			return
		}
		p.printf("%s (%d entries) {", x.Type(), x.Len())
		if x.Len() > 0 {
			p.indent++
			p.printf("\n")
			for i, n := 0, x.Len(); i < n; i++ {
				p.printf("%d: ", i)
				p.dump(x.Index(i), nil)
				p.printf("\n")
			}
			p.indent--
		}
		p.printf("}")

	case reflect.Struct:
		typ := x.Type()

		// 如果span，确定：=x.Interface（）（lexical.span）；嗯{
		// p、 printf（“%s”、&span）
		// 回来
		// }

		p.printf("%s {", typ)
		p.indent++

		first := true
		if n != nil {
			p.printf("\n")
			first = false
			// p、 printf（“Span:%s\n”，n.Span（））
			// 如果c:=*n.注释（）；c！=零{
			// p、 printf（“注释：”）
			// p、 转储（反映（c）的值，无）
			// p、 printf（“\n”）
			// }
		}

		for i, n := 0, typ.NumField(); i < n; i++ {
			// 排除未导出的字段，因为其
			// 无法通过反射访问值。
			if name := typ.Field(i).Name; isExported(name) {
				if first {
					p.printf("\n")
					first = false
				}
				p.printf("%s: ", name)
				p.dump(x.Field(i), nil)
				p.printf("\n")
			}
		}

		p.indent--
		p.printf("}")

	default:
		switch x := x.Interface().(type) {
		case string:
			// 用引号打印字符串
			p.printf("%q", x)
		default:
			p.printf("%v", x)
		}
	}
}

func isExported(name string) bool {
	ch, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(ch)
}
